package com.xiaotu.spider.baidu.pageprocessor;

import com.xiaotu.common.exception.SpiderException;
import com.xiaotu.common.exception.SpiderExceptionCode;
import com.xiaotu.common.util.*;
import com.xiaotu.spider.AUrlPageProcessor;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.Page;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

/**
 * @类名 BaiduMapPageProcessor
 * @日期 2017年2月15日
 * @作者 高海军
 * @功能 百度地图地理信息检索
 */
@Component("BaiduMapPageProcessor")
public class BaiduMapPageProcessor extends AUrlPageProcessor
{

    private static final int PAGE_SIZE = 20;
    private static final String SEARCH_URL = "http://api.map.baidu.com/place/v2/search?" +
            "query=%s&page_num=%s&page_size=" + PAGE_SIZE + "&output=json&scope=2&" +
            "region=%s&ak=lL0YPzHCf7AL7KUR2upitKRX5tNKj7Y1";

    private static final String CHINA = "全国";

    private String keyWord = null;

    private String jsonFile = null;
    private String textFile = null;

    private String cityCodeFile = null;

    private static final Set<String> UID_SET = ConcurrentHashMap.newKeySet();

    @Override
    public void initRunParams(Map<String, Object> map)
    {
        if (!map.containsKey("keyword"))
            throw new SpiderException(SpiderExceptionCode.PageProcessorParameterErrorException);
        this.keyWord = KeywordEncode.encode(map.get("keyword").toString());

        if (!map.containsKey("json"))
            throw new SpiderException(SpiderExceptionCode.PageProcessorParameterErrorException);
        this.jsonFile = map.get("json").toString();

        if (!map.containsKey("txt"))
            throw new SpiderException(SpiderExceptionCode.PageProcessorParameterErrorException);
        this.textFile = map.get("txt").toString();

        if (map.containsKey("code"))
            this.cityCodeFile = map.get("code").toString();
    }

    @Override
    public void process(Page page)
    {
        switch (this.getPageDataType(page))
        {
            case Constants.PageDataType.TV_RECORD:
                this.areaProcess(page);
                break;
            case Constants.PageDataType.SEARCH_LIST:
                this.chinaProcess(page);
                break;
        }
    }

    private void chinaProcess(Page page)
    {
        this.getResults(page).forEach(oneMap -> PageUtils.addTargetRequest(page, String.format
                (SEARCH_URL, this.keyWord, 0, KeywordEncode.encode(oneMap.get("name").toString())
                ), this.getPageMap(page, oneMap.get("name").toString())));
    }

    private Map<String, Object> getPageMap(Page page, String region)
    {
        String area = page.getRequest().getExtra("area").toString();
        if (!area.contains(region))
            area += "," + region;
        return PageUtils.getNewExtraMapFromPage(page, new String[]{"area"}, new String[]{area});
    }

    private void areaProcess(Page page)
    {
        String pageNum = RegexUtils.regexesSearch(page.getUrl().toString(), "page_num=" +
                RegexUtils.REGEX_INTEGER, RegexUtils.REGEX_INTEGER);
        Map<String, Object> areaMap = GsonUtils.fromJson(page.getRawText(), Map.class);
        int pageNo = Integer.parseInt(pageNum);
        int total = Float.valueOf(StringUtil.nullToString(areaMap.get("total"))).intValue();
        if (total > PAGE_SIZE && pageNo == 0)
        {
            String area = RegexUtils.regexesSearch(KeywordEncode.decode(page.getUrl().toString())
                    , "region=" + RegexUtils.REGEX_CH, RegexUtils.REGEX_CH);
            int pageSize = total % PAGE_SIZE == 0 ? total / PAGE_SIZE : total / PAGE_SIZE + 1;
            IntStream.range(1, pageSize).forEach(i -> PageUtils.addTargetRequest(page, String
                    .format(SEARCH_URL, this.keyWord, i, KeywordEncode.encode(area)), this
                    .getPageMap(page, area)));
        }

        this.getResults(page).forEach(oneMap ->
        {
            if (UID_SET.contains(oneMap.get("uid").toString()))
                return;
            UID_SET.add(oneMap.get("uid").toString());
            FileUtil.writeFile(this.jsonFile, GsonUtils.toJson(oneMap) + SepratorUtil
                    .LINE_SEPRATOR);
            Map<String, Object> detailMap = (Map<String, Object>) oneMap.get("detail_info");
            detailMap = detailMap == null ? new HashMap<>() : detailMap;
            String[] fields = new String[]{StringUtil.nullToString(oneMap.get("uid")), page
                    .getRequest().getExtra("area").toString(), StringUtil.nullToString(oneMap.get
                    ("name")), StringUtil.nullToString(oneMap.get("address")), StringUtil
                    .nullToString(detailMap.get("tag")), StringUtil.nullToString(oneMap.get
                    ("telephone")), StringUtil.nullToString(detailMap.get("detail_url"))};
            FileUtil.writeFile(this.textFile, StringUtils.join(fields, SepratorUtil.SEP_PIPE) +
                    SepratorUtil.LINE_SEPRATOR);
        });
    }

    private List<Map<String, Object>> getResults(Page page)
    {
        Map<String, Object> areaMap = GsonUtils.fromJson(page.getRawText(), Map.class);
        List<Map<String, Object>> resultList = (List<Map<String, Object>>) areaMap.get("results");
        if (resultList == null || resultList.isEmpty())
            throw new SpiderException(SpiderExceptionCode.DataInvalideException);
        return resultList;
    }

    @Override
    protected String[] getStartRequestUrls()
    {
        return null;
    }

    @Override
    protected List<JSONObject> getStartRequestUrlsAndParams()
    {
        List<JSONObject> list = new ArrayList<>();

        if (StringUtils.isNotEmpty(this.cityCodeFile))
        {
            List<String> lineList = FileUtil.readFileByLine(this.cityCodeFile,"UTF-8");
            lineList.stream().skip(1).forEach(line ->
            {
                String[] codeArr = line.split(",");
                JSONObject obj = new JSONObject();
                obj.put(URL_KEY, String.format(SEARCH_URL, this.keyWord, 0, KeywordEncode.encode
                        (codeArr[1])));
                obj.put("area", codeArr[1]);
                list.add(obj);
            });
            return list;
        }

        JSONObject obj = new JSONObject();
        obj.put(URL_KEY, String.format(SEARCH_URL, this.keyWord, 0, KeywordEncode.encode(CHINA)));
        obj.put("area", CHINA);
        list.add(obj);
        return list;
    }

    @Override
    public String getPageDataType(Page page)
    {
        Map<String, Object> map = this.getResults(page).get(0);
        if (map.size() == 2 && map.containsKey("name") && map.containsKey("num"))
            return Constants.PageDataType.SEARCH_LIST;
        return Constants.PageDataType.TV_RECORD;
    }

    @Override
    protected String getSpiderType()
    {
        return "BaiduMap";
    }
}
